package com.hb.common.utils.http;


import com.hb.common.config.ProxyConfig;
import com.hb.common.constants.Constants;
import com.hb.common.utils.BeanUtils;
import com.hb.common.utils.StringUtil;
import com.hb.common.utils.MyX509TrustManager;
import com.hb.common.utils.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.X509Certificate;
import java.util.regex.Pattern;

/**
 * 通用http发送方法
 *
 * @author hb
 */
public class HttpUtils
{
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    // \b 是单词边界(连着的两个(字母字符 与 非字母字符) 之间的逻辑上的间隔),
    // 字符串在编译时会被转码一次,所以是 "\\b"
    // \B 是单词内部逻辑间隔(连着的两个字母字符之间的逻辑上的间隔)
    private static final String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i"
            +"|windows (phone|ce)|blackberry"
            +"|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp"
            +"|laystation portable)|nokia|fennec|htc[-_]"
            +"|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";

    private static final String tabletReg = "\\b(ipad|tablet|(Nexus 7)|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";

    //移动设备正则匹配：手机端、平板
    private static Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE);
    private static Pattern tabletPat = Pattern.compile(tabletReg, Pattern.CASE_INSENSITIVE);

    /**
     * 获取当前请求的request对象
     * @return
     */
    public static HttpServletRequest getRequest() {
        RequestAttributes requestAttributes = null;
        try{
            requestAttributes = RequestContextHolder.currentRequestAttributes();
        }catch (IllegalStateException e){
            return null;
        }
        return ((ServletRequestAttributes) requestAttributes).getRequest();
    }

    /**
     * 获取当前请求的参数
     * @param name
     * @return
     */
    public static String getRequestParameter(String name) {
        HttpServletRequest request = getRequest();
        if(BeanUtils.isEmpty(request)) {
            return null;
        }
        return request.getParameter(name);
    }



    /**
     * 下载文件。
     *
     * @param response
     * @param fullPath
     *            下载文件路径
     * @param fileName
     *            下载文件名
     * @throws IOException
     *             void
     */
    public static void downLoadFile(HttpServletResponse response, String fullPath, String fileName) throws IOException {
        OutputStream outp = response.getOutputStream();
        File file = new File(fullPath);
        if (file.exists()) {
            response.setContentType("application/x-download");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            if (System.getProperty("file.encoding").equals("GBK")) {
                response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(), "ISO-8859-1"));
            } else {
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            }
            try (FileInputStream in = new FileInputStream(fullPath);){
                IOUtils.copy(in, outp);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (outp != null) {
                    outp.close();
                    outp = null;
                    response.flushBuffer();
                }
            }
        } else {
            outp.write("文件不存在!".getBytes("utf-8"));
        }
    }




    /**
     * 发送请求。
     *
     * @param url
     *            URL地址
     * @param params
     *            发送参数
     * @param requestMethod
     *            GET,POST
     * @return
     * @throws KeyManagementException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws IOException
     */
    public static String sendHttpsRequest(String url, String params,
                                          String requestMethod) {
        HttpsURLConnection conn;
        String str = null;
        try {
            conn = getHttpsConnection(url);
            conn.setRequestMethod(requestMethod);

            conn.setDoInput(true);
            conn.setDoOutput(true);

            if (StringUtil.isNotEmpty(params)) {
                OutputStream outputStream = conn.getOutputStream();
                outputStream.write(params.getBytes("utf-8"));
                outputStream.close();
            }
            str = getOutPut(conn);
        } catch (KeyManagementException e) {
            throw new RuntimeException("远程服务器请求失败！"+e.getMessage(),e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("远程服务器请求失败！"+e.getMessage(),e);
        } catch (NoSuchProviderException e) {
            throw new RuntimeException("远程服务器请求失败！"+e.getMessage(),e);
        } catch (IOException e) {
            throw new RuntimeException("远程服务器请求失败！"+e.getMessage(),e);
        }

        return str;
    }

    /**
     * 获取https连接。
     * @param accessUrl
     * @return
     * @throws KeyManagementException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws IOException
     */
    public static HttpsURLConnection getHttpsConnection(String accessUrl)
            throws KeyManagementException, NoSuchAlgorithmException,
            NoSuchProviderException, IOException {
        URL url = new URL(accessUrl);
        HttpsURLConnection connection = (HttpsURLConnection) url
                .openConnection();

        TrustManager[] tm = { new MyX509TrustManager() };

        SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
        sslContext.init(null, tm, new java.security.SecureRandom());
        SSLSocketFactory ssf = sslContext.getSocketFactory();
        connection.setSSLSocketFactory(ssf);
        return connection;
    }


    /**
     * 读取返回数据。
     *
     * @param conn
     * @return
     * @throws IOException
     */
    public static String getOutPut(HttpsURLConnection conn) throws IOException {
        InputStream inputStream = conn.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(
                inputStream, "utf-8");
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        StringBuffer buffer = new StringBuffer();
        String str = null;
        while ((str = bufferedReader.readLine()) != null) {
            buffer.append(str);
        }
        bufferedReader.close();
        inputStreamReader.close();
        inputStream.close();
        conn.disconnect();
        return buffer.toString();
    }



    public static String sendData(String url,String data){
        return sendData( url, data, "utf-8");
    }

    /**
     * 发送数据到指定的URL并读取返回结果。
     * @param url
     * @param data
     * @return
     */
    public static String sendData(String url,String data,String charset){
        URL uRL;
        URLConnection conn;

        BufferedReader bufferedReader = null;
        try {
            uRL = new URL(url);
            conn = uRL.openConnection();
            conn.setDoOutput(true);
            if(StringUtil.isNotEmpty(data)){
                OutputStream stream=conn.getOutputStream();
                stream.write(data.getBytes(charset));
                stream.flush();
                stream.close();
            }


            // Get the response
            bufferedReader = new BufferedReader(new InputStreamReader(
                    conn.getInputStream()));
            StringBuffer response = new StringBuffer();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                response.append(line);
            }

            bufferedReader.close();

            return response.toString();
        }
        catch (MalformedURLException e) {
            e.printStackTrace();
            return "";
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 检测是否是移动设备访问
     *
     * @return true:移动设备接入，false:pc端接入
     */
    public static boolean isMobile(HttpServletRequest request){
        String userAgent = request.getHeader("user-agent");
        if(null == userAgent){
            userAgent = "";
        }
        return phonePat.matcher(userAgent).find() || tabletPat.matcher(userAgent).find();
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url)
    {
        return sendGet(url, null, Constants.UTF8, 0);
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param)
    {
        return sendGet(url, param, Constants.UTF8, 0);
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @param retries 重试次数
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String contentType, int retries)
    {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        URLConnection connection;
        String ip = null;
        int port = 0;
        boolean successFlag = false;
        try
        {
            String urlNameString;
            if (StringUtils.isNotBlank(param) && !param.startsWith("?")) {
                urlNameString = url + "?" + param;
            } else {
                urlNameString = url;
            }
//            log.info("sendGet : {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            if (ProxyConfig.isFlag()) {
                ProxyConfig.IpHost ipHost = ProxyConfig.getIpHost();
                ip = ipHost.getIp();
                port = ipHost.getPort();
                if (retries > 0) {
                    log.error("第{}次重试：url:{}  param:{}", ProxyConfig.getNum()-retries, url, param);
                    log.error("proxy iphost:{}:{}", ip, port);
                }
                Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipHost.getIp(), ipHost.getPort()));
                connection = realUrl.openConnection(proxy);
            } else {
                connection = realUrl.openConnection();
            }
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();

            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            successFlag = true;
            //log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (in != null)
                {
                    in.close();
                }
            }
            catch (Exception ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
            //若开启代理 配置多个代理服务，请求失败进入重试阶段（自动）
            if (!successFlag) {
                if (ProxyConfig.isFlag()) {
                    if (retries == 0) {
                        return sendGet(url, param, Constants.UTF8, ProxyConfig.getNum()-1);
                    } else if (retries > 1) {
                        return sendGet(url, param, Constants.UTF8, retries-1);
                    }
                } else if (retries >= 1) {
                    //没开启代理 手动重试
                    return sendGet(url, param, Constants.UTF8, retries-1);
                }
            }
            else {
                successFlag = false;
                return result.toString();
            }
        }
        successFlag = false;
        return result.toString();
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param)
    {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try
        {
            String urlNameString = url;
            log.info("sendPost - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            //log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (out != null)
                {
                    out.close();
                }
                if (in != null)
                {
                    in.close();
                }
            }
            catch (IOException ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    public static String sendSSLPost(String url, String param)
    {
        StringBuilder result = new StringBuilder();
        String urlNameString = url + "?" + param;
        try
        {
            log.info("sendSSLPost - {}", urlNameString);
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
            URL console = new URL(urlNameString);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);

            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null)
            {
                if (ret != null && !"".equals(ret.trim()))
                {
                    result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
                }
            }
            log.info("recv - {}", result);
            conn.disconnect();
            br.close();
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }

    private static class TrustAnyTrustManager implements X509TrustManager
    {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
        {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
        {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            return new X509Certificate[] {};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier
    {
        @Override
        public boolean verify(String hostname, SSLSession session)
        {
            return true;
        }
    }
}
